package com.xyy.saas.payment.adpater.impl;

import com.alibaba.fastjson.JSONObject;
import com.xyy.saas.payment.adpater.PaymentAdapter;
import com.xyy.saas.payment.adpater.abchinaLoan.core.req.OrderChangeDto;
import com.xyy.saas.payment.adpater.abchinaLoan.core.req.OrderCheckDto;
import com.xyy.saas.payment.adpater.abchinaLoan.core.req.QueryLoanDto;
import com.xyy.saas.payment.adpater.abchinaLoan.core.resp.OrderChangeNotifyResp;
import com.xyy.saas.payment.adpater.abchinaLoan.core.resp.OrderCheckResp;
import com.xyy.saas.payment.adpater.abchinaLoan.service.AbchinaLoanService;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.bo.*;
import com.xyy.saas.payment.cores.enums.AbchinaLoanOrderCheckResEnum;
import com.xyy.saas.payment.cores.enums.AbchinaLoanOrderStatusEnum;
import com.xyy.saas.payment.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.cores.enums.TransactionStatusEnum;
import com.xyy.saas.payment.cores.param.TransactionParam;
import com.xyy.saas.payment.cores.vo.FinanceAccountInfoVo;
import com.xyy.saas.payment.dao.model.PrePayOrder;
import com.xyy.saas.payment.dao.model.RefundOrder;
import com.xyy.saas.payment.payment.core.bo.RefundBO;
import com.xyy.saas.payment.payment.core.dto.OrderClosedDto;
import com.xyy.saas.payment.payment.core.dto.PayDto;
import com.xyy.saas.payment.payment.core.dto.PrepayDto;
import com.xyy.saas.payment.payment.core.dto.RefundDto;
import com.xyy.saas.payment.payment.core.service.PrePayOrderService;
import com.xyy.saas.payment.util.AmountCastUtil;
import com.xyy.saas.payment.util.DateUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Objects;

/**
 * @author wangzeqi
 */
@Slf4j
@Component
public class AbchinaLoanAdapter implements PaymentAdapter {

    @Resource
    private AbchinaLoanService abchinaLoanService;
    @Resource
    private PrePayOrderService prePayOrderService;
    /*@Value("${abchinaLoan.app.directUrl:https://wx.abchina.com/webank/main-view/h5ForKED?id=SlTzmeaAHEc%3D}")
    private String abchinaLoanAppDirectUrl;*/
    @Value("${abchinaLoan.test.switch:false}")
    private Boolean abchinaLoanTest;


    @Override
    public PrepayBo prepay(PrepayDto prepayDto) throws PaymentException {
        return prePayOrderService.prepayOrderByPrepayDto(prepayDto);
    }

    @SneakyThrows
    @Override
    public RefundBO notifyRefund(RefundDto refundDto) {
        OrderChangeDto orderChangeDto = new OrderChangeDto()
                .setOrderId(refundDto.getPayNo())
                .setChgType("1")
                .setRefundFlowNo(refundDto.getRefundNo())
                .setRefundType(convertAbchinaLoanRefundType(refundDto.getRefundType()))
                .setRefundAmt(AmountCastUtil.castFen2YuanByBigDecimal(refundDto.getAmount()))
                .setTransTime(DateUtil.PATTERN_DATE.format(LocalDateTime.now()));
        OrderChangeNotifyResp orderChangeNotifyResp = abchinaLoanService.orderChangeNotify(orderChangeDto);
        if (orderChangeNotifyResp == null || StringUtils.isBlank(orderChangeNotifyResp.getConfirmRetCode())) {
            throw new PaymentException(ResultCodeEnum.ABCHINA_ORDERCHANGENOTIFY_RESPONSE_NULL);
        }
        if (!Objects.equals(orderChangeNotifyResp.getConfirmRetCode(), OrderChangeNotifyResp.ORDER_CANCEL_SUCCESS)) {
            throw new PaymentException(ResultCodeEnum.ABCHINA_ORDERCHANGENOTIFY_RESULT_FAIL, StringUtils.isNotBlank(orderChangeNotifyResp.getConfirmRetMsg()) ?
                    orderChangeNotifyResp.getConfirmRetMsg() : ResultCodeEnum.ABCHINA_ORDERCHANGENOTIFY_RESULT_FAIL.getDisplayMsg());
        }

        RefundBO refundBO = new RefundBO();
        refundBO.setStatus(RefundOrder.STATUS_FINISH);
        return refundBO;
    }

    @SneakyThrows
    private String convertAbchinaLoanRefundType(Integer refundType) {
        if (refundType == null) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "refundType is null");
        }
        if (refundType != 1 && refundType != 2) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "refundType error");
        }
        // 农行入参 0-部分退  1-全退
        return refundType == 1 ? "0" : "1";
    }

    @Override
    public PayBo pay(PayDto payDto) throws PaymentException {
        // mock支付
        if (Boolean.TRUE.equals(abchinaLoanTest)) {
            return createPayBo(payDto.getPayNo());
        }
        // 订单核验
        OrderCheckDto orderCheckDto = new OrderCheckDto();
        fillPayOrderCheckDto(orderCheckDto, payDto);
        OrderCheckResp orderCheckResp = abchinaLoanService.orderCheck(orderCheckDto);
        validateOrderCheckResponse(orderCheckResp);
        // 核验结果解析
        handleOrderCheckResult(payDto.getPayNo(), orderCheckResp);

        return createPayBo(payDto.getPayNo());
    }

    private void validateOrderCheckResponse(OrderCheckResp orderCheckResp) throws PaymentException {
        if (orderCheckResp == null || StringUtils.isBlank(orderCheckResp.getCheckRes())) {
            throw new PaymentException(ResultCodeEnum.ABCHINA_ORDERCHECK_RESPONSE_NULL);
        }
    }

    private void handleOrderCheckResult(String payNo, OrderCheckResp orderCheckResp) throws PaymentException {
        AbchinaLoanOrderCheckResEnum checkResEnum = AbchinaLoanOrderCheckResEnum.fromCode(orderCheckResp.getCheckRes());
        if (checkResEnum == null) {
            throw new PaymentException(ResultCodeEnum.ABCHINA_ORDERCHECK_RESULT_UNKNOWN);
        }
        switch (checkResEnum) {
            case CONTRACT_STATUS_INVALID:
                throw new PaymentException(ResultCodeEnum.ABCHINA_ORDERCHECK_CONTRACT_STATUS_INVALID);
            case INSUFFICIENT_BALANCE:
                throw new PaymentException(ResultCodeEnum.ABCHINA_ORDERCHECK_INSUFFICIENT_BALANCE);
            case INVALID_PAYMENT_ACCOUNT:
                throw new PaymentException(ResultCodeEnum.ABCHINA_ORDERCHECK_INVALID_PAYMENT_ACCOUNT);
            case UNPAID_ORDER_EXISTS:
                log.error("农行订单支付资质核验存在未支付订单, payNo:{}, 未支付订单:{}", payNo, orderCheckResp.getTrCode());
                throw new PaymentException(ResultCodeEnum.ABCHINA_ORDERCHECK_UNPAID_ORDER_EXISTS);
            case VERIFIED_PASS:
                break; // 校验通过，继续执行
            default:
                throw new PaymentException(ResultCodeEnum.ABCHINA_ORDERCHECK_RESULT_UNPASS, checkResEnum.getDescription());
        }
    }

    private PayBo createPayBo(String payNo) {
        PayBo payBo = new PayBo();
        payBo.setPayNO(payNo);
        //payBo.setPayLoad(new JSONObject().fluentPut("abchinaLoanAppDirectUrl", abchinaLoanAppDirectUrl).toJSONString());
        return payBo;
    }

    private void fillPayOrderCheckDto(OrderCheckDto orderCheckDto, PayDto payDto) {
        orderCheckDto.setOrderId(payDto.getPayNo())
                .setUscc(payDto.getMerchantUscc())
                .setOrderAmt(AmountCastUtil.castFen2YuanByBigDecimal(payDto.getAmount()))
                .setEntrustedAcc(payDto.getReceiveAccount());
    }

    @Override
    public OrderClosedBo orderClosed(OrderClosedDto orderClosedDto) throws PaymentException {
        if (Boolean.TRUE.equals(abchinaLoanTest)) {
            return new OrderClosedBo().setPayNo(orderClosedDto.getPayNo()).setStatus(OrderClosedBo.SUCCESS);
        }

        OrderChangeDto orderChangeDto = new OrderChangeDto();
        fillOrderClosedDto(orderChangeDto, orderClosedDto);
        OrderChangeNotifyResp orderChangeNotifyResp = abchinaLoanService.orderChangeNotify(orderChangeDto);
        if (orderChangeNotifyResp == null || StringUtils.isBlank(orderChangeNotifyResp.getConfirmRetCode())) {
            throw new PaymentException(ResultCodeEnum.ABCHINA_ORDERCHANGENOTIFY_RESPONSE_NULL);
        }
        if (!Objects.equals(orderChangeNotifyResp.getConfirmRetCode(), OrderChangeNotifyResp.ORDER_CANCEL_SUCCESS)) {
            throw new PaymentException(ResultCodeEnum.ABCHINA_ORDERCHANGENOTIFY_RESULT_FAIL, StringUtils.isNotBlank(orderChangeNotifyResp.getConfirmRetMsg()) ?
                    orderChangeNotifyResp.getConfirmRetMsg() : ResultCodeEnum.ABCHINA_ORDERCHANGENOTIFY_RESULT_FAIL.getDisplayMsg());
        }
        OrderClosedBo orderClosedBo = new OrderClosedBo().setPayNo(orderClosedDto.getPayNo()).setStatus(OrderClosedBo.SUCCESS);
        return orderClosedBo;
    }

    private void fillOrderClosedDto(OrderChangeDto orderChangeDto, OrderClosedDto orderClosedDto) {
        orderChangeDto.setOrderId(orderClosedDto.getPayNo())
                .setChgType("0");
    }

    @Override
    public QueryResultBo queryPayStatus(String tradeNo, PrePayOrder prePayOrder) throws PaymentException {
        // todo
        return null;
    }

    @Override
    public TransactionResultBO queryTransaction(TransactionParam param) throws PaymentException {
        if (StringUtils.isBlank(param.getTradeNo())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }

        if (Boolean.TRUE.equals(abchinaLoanTest)) {
            return mockQueryTransactionSuccess(param);
        }

        OrderChangeNotifyResp orderChangeNotifyResp = abchinaLoanService.queryOrderStatus(param.getTradeNo());
        if (orderChangeNotifyResp == null || StringUtils.isBlank(orderChangeNotifyResp.getConfirmRetCode())) {
            throw new PaymentException(ResultCodeEnum.ABCHINA_QUERYORDERSTATUS_RESPONSE_NULL);
        }
        String confirmRetCode = orderChangeNotifyResp.getConfirmRetCode();
        AbchinaLoanOrderStatusEnum abchinaLoanOrderStatusEnum = AbchinaLoanOrderStatusEnum.fromCode(confirmRetCode);
        if (abchinaLoanOrderStatusEnum == null) {
            throw new PaymentException(ResultCodeEnum.ABCHINA_QUERYORDERSTATUS_RESULT_UNKNOWN,
                    StringUtils.isNotBlank(orderChangeNotifyResp.getConfirmRetMsg()) ? orderChangeNotifyResp.getConfirmRetMsg() : ResultCodeEnum.ABCHINA_QUERYORDERSTATUS_RESULT_UNKNOWN.getDisplayMsg());
        }
        String payTime = orderChangeNotifyResp.getPayTime();
        Long successTime = StringUtils.isNotBlank(payTime) ? DateUtil.toMilli(payTime) : Long.valueOf(0L);
        TransactionResultBO queryResultBo = getTransactionResultBO(param, abchinaLoanOrderStatusEnum, successTime);
        //queryResultBo.setAmount(resultBo.getAmount());
        return queryResultBo;
    }

    /**
     * 贷款申请状态查询
     */
    public FinanceAccountInfoVo queryLoanStatus(String uscc) {
        QueryLoanDto loanDto = new QueryLoanDto();
        loanDto.setUscc(uscc);
        loanDto.checkParam();
        String loanStatus = abchinaLoanService.queryLoanStatus(loanDto).getLoanStatus();
        return FinanceAccountInfoVo.builder().loanStatus(loanStatus).build();
    }

    private TransactionResultBO getTransactionResultBO(TransactionParam param, AbchinaLoanOrderStatusEnum abchinaLoanOrderStatusEnum, Long successTime) {
        TransactionStatusEnum transactionStatusEnum = TransactionStatusEnum.convertAbchinaLoan(abchinaLoanOrderStatusEnum);
        TransactionResultBO queryResultBo = new TransactionResultBO();
        queryResultBo.setTradeNo(param.getTradeNo());
        queryResultBo.setStatus(transactionStatusEnum != null ? transactionStatusEnum.getCode() : null);
        queryResultBo.setSuccessTime(successTime);
        queryResultBo.setChannelChargeNo(param.getTradeNo());
        queryResultBo.setChannel(param.getChannel());
        return queryResultBo;
    }

    private TransactionResultBO mockQueryTransactionSuccess(TransactionParam param) {
        TransactionResultBO queryResultBo = new TransactionResultBO();
        queryResultBo.setTradeNo(param.getTradeNo());
        queryResultBo.setStatus(TransactionStatusEnum.SUCCESS.getCode());
        queryResultBo.setSuccessTime(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
        queryResultBo.setChannelChargeNo(param.getTradeNo());
        queryResultBo.setChannel(param.getChannel());
        //queryResultBo.setAmount(resultBo.getAmount());
        return queryResultBo;
    }

}
